#pragma once

#include<iostream>
#include<queue>
#include<pthread.h>

template <class T>
class BlockQueue
{
    //缺省值
    static const int defalutnum = 20;
public:
    //构造
    BlockQueue(int maxcap=defalutnum):maxcap_(maxcap)
    {
        pthread_mutex_init(&mutex_,nullptr);
        pthread_cond_init(&c_cond_,nullptr);
        pthread_cond_init(&p_cond_,nullptr);
        low_water_=maxcap_/3;
        high_water_=maxcap_*2/3;
    }
    //出队
    T pop()
    {
        //加锁
        pthread_mutex_lock(&mutex_);//拿到锁也不一定能消费，要确保消费条件满足
        while(q_.size() == 0)//防止线程被伪唤醒,用while
        {
            //条件变量
            pthread_cond_wait(&c_cond_,&mutex_);
            
        }
        T out = q_.front();
        q_.pop();
        if(q_.size() == low_water_)
        {
            //消费者能保证有空间
            //唤醒
            pthread_cond_signal(&p_cond_);
            //如果线程wait,被误唤醒了呢
            //比如多线程，比如pthread_cond_broadcast,
            //生产者被唤醒的第一个处理了判断条件，唤醒的第二个无法生产入队，称为伪唤醒
            
        }
        //解锁
        pthread_mutex_unlock(&mutex_);
        return out;
    }
    //入队
    void push(const T& in)
    {
        //加锁
        pthread_mutex_lock(&mutex_);//拿到锁也不一定能生产，要确保生产条件满足
        while(q_.size() == maxcap_)//判断也要访问临界资源，要在加锁之后判断
        {
            //条件变量  1.调用的时候，自动释放锁    2.因为唤醒而返回的时候，重新持有锁
            pthread_cond_wait(&p_cond_,&mutex_);
         
        }
        //走到这
        //1.队列没满 2.被唤醒了
        q_.push(in);
        if(q_.size() == high_water_)
        {
            //生产者能保证有数据
            //唤醒
            pthread_cond_signal(&c_cond_);
        }
        //解锁
        pthread_mutex_unlock(&mutex_);
    }
    //析构
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&c_cond_);
        pthread_cond_destroy(&p_cond_);
    }

private:
    std::queue<T> q_;//共享资源
    int maxcap_;//队列当中的极值
    //int mincap_;
    pthread_mutex_t mutex_;//锁--互斥
    pthread_cond_t c_cond_;//条件变量--同步
    pthread_cond_t p_cond_;

    //水位线
    int low_water_;
    int high_water_;
};